package lox

class LoxFunction(
    private val declaration: Stmt.Function, private val closure: Environment, private val isInitializer: Boolean
) : LoxCallable {

    override fun call(interpreter: Interpreter, arguments: List<Any?>): Any? = Environment(closure).apply {
        declaration.params.withIndex().forEach { define(it.value.lexeme, arguments[it.index]) }
    }.run {
        try {
            interpreter.executeBlock(declaration.body, this)
        } catch (returnValue: Return) {
            return@run if (isInitializer) closure.getAt(0, "this") else returnValue.value
        }

        if (isInitializer) {
            return@run closure.getAt(0, "this")
        }

        Unit
    }


    override fun arity(): Int = declaration.params.size
    override fun toString(): String = "<fn ${declaration.name.lexeme}>"

    fun bind(instance: LoxInstance): LoxFunction =
        Environment(closure).also { it.define("this", instance) }.run { LoxFunction(declaration, this, isInitializer) }

}